;; https://projecteuler.net/problem=6

;; Sum square difference

;; Problem 6

;; The sum of the squares of the first ten natural numbers
;; is, $$1^2 + 2^2 + ... + 10^2 = 385$$

;; The square of the sum of the first ten natural numbers
;; is, $$(1 + 2 + ... + 10)^2 = 55^2 = 3025$$

;; Hence the difference between the sum of the squares of
;; the first ten natural numbers and the square of the sum
;; is $3025 - 385 = 2640$.

;; Find the difference between the sum of the squares of the
;; first one hundred natural numbers and the square of the
;; sum.


(import
  (except (rnrs base) let-values)
  (only (guile) lambda* λ)
  (srfi srfi-1)
  (contract)
  (lib segment)
  (lib parallelism)
  (prefix (lib math) math:))


(define-with-contract sum-of-squares
  (require (>= start 0)
           (>= end 0)
           (< start end)
           (integer? start)
           (integer? end))
  (ensure (> <?> 0)
          (integer? <?>))
  (λ (start end)
    (let iter ([sum 0] [num start])
      (cond
       [(= num end)
        (+ sum (math:square num))]
       [else
        (iter (+ sum (math:square num))
              (+ num 1))]))))


(define get-sum-of-squares
  (λ (segments)
    (run-in-parallel segments
                     ;; Calculate the sum of squares
                     ;; for each segment.
                     (λ (seg)
                       (sum-of-squares (segment-start seg)
                                       (segment-end seg)))
                     ;; Sum up all the sums.
                     (λ (acc current)
                       (+ acc current))
                     ;; Sum starts with 0.
                     0)))


(define get-square-of-sums
  (λ (segments)
    ;; Calculate square of sums.
    (math:square
     (run-in-parallel segments
                      ;; Calculate the sum for each
                      ;; segment.
                      (λ (seg)
                        (math:range-sum (segment-start seg)
                                        (segment-end seg)))
                      ;; Sum all segment sums.
                      (λ (acc current)
                        (+ acc current))
                      ;; Sum starts with 0.
                      0))))


(define-with-contract diff-sum-sq-sq-sum
  (require (>= start 0)
           (>= end 0)
           (< start end))
  (ensure (> <?> 0))
  (λ (start end)
    ;; First split the range into segments.
    (let ([segment-count 8])
      (let ([segments (segment start end segment-count)])
        (abs
         (- (get-sum-of-squares segments)
            (get-square-of-sums segments)))))))


(let* ([start 1]
       [end (expt 10 2)])
  (display
   (simple-format
    #f "~a\n"
    (diff-sum-sq-sq-sum start end))))
